﻿/*
* Project WarRoom
* https://warroom.codeplex.com/
*
* Copyright 2012, Yi-Fan Liao
*
* This file is part of Project WarRoom.
*
* Project WarRoom is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
* 
* Project WarRoom is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
* 
* You should have received a copy of the GNU General Public License
* along with Project WarRoom.  If not, see <http://www.gnu.org/licenses/>.
*
* Date: Fri Aug 24 21:41:08 2012 +0800
*/
(function ($) {

    // Declare static members here
    var replaceURLWithHTMLLinks = function (text) {
        var exp = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig;
        return text.replace(exp, "<a href='$1' target='_blank'>$1</a>");
    };

    $.widget("warroom.chatroom", {

        // Private members
        isMouseOver: false,

        // DOM 
        header: null,
        userList: null,
        messageContainer: null,
        messageList: null,
        newMessage: null,

        // Unread message count
        unreadMessageCount: 0,

        // Latest message 
        latestMessage: {
            "MessageID": null,
            "Sender": null,
            "Content": null,
            "SubmitDateTime": null
        },

        // These options will be used as defaults
        options: {
            visible: true,
            draggable: true,
            cssClass: "warroom.chatroom",

            group: null,

            // JSON end points
            getMessagesUrl: null,
            getNewMessagesUrl: null,
            submitMessageUrl: null,
            getUserListUrl: null,

            // The callback to alert new messages
            alertNewMessages: null,
            stopAlert: null,

            // Pass in a method for centralizing ajax request and data cache.
            // Default to $.ajax when sendRequest is undefined.
            sendAjaxRequest: function (ajaxOptions) { $.ajax(ajaxOptions); }
        },

        // Set up the widget
        _create: function () {

            this._setOption("visible", this.options.visible);
            this._setOption("draggable", this.options.draggable);
            this._initializeChatroom();
            this._bindEvents();
            this._getMessages();
            this._getOnlineUsers();

        },

        // The _setOption method responds to changes to options
        // It's not called for the options passed in during widget creation
        _setOption: function (key, value) {
            switch (key) {
                case "visible":
                    this._toggleChatroom(value)
                    break;
                case "draggable":
                    if (value === true) {
                        this.element.draggable({
                            handle: '#chatroom-header'
                        });
                    }
                    break;
                case "cssClass":
                    if (value !== "") {
                        this.element.addClass(value);
                    }
                    break;
            }

            $.Widget.prototype._setOption.apply(this, arguments);
        },

        // Bind DOM objects as jQuery objects
        _initializeChatroom: function () {
            //            this.header = $("#chatroom-header");
            this.messageList = $("#messages");
            this.newMessage = $("#newMessage");
            this.userList = $("#users");
            this.messageContainer = $("#messages-container");
        },

        _getOnlineUsers: function () {
            var that = this;

            this.options.sendAjaxRequest({
                type: "POST",
                url: that.options.getUserListUrl,
                data: "{}",
                contentType: "application/json; charset=utf-8",
                dataType: "json",
                error: function () {
                    that.element.queue("onlineUsersRequest", that._getOnlineUsers());
                    that.element.dequeue("onlineUsersRequest");
                },
                success: function (result) {
                    if (result.d != null) {
                        // Show online users using accordion widget, temporarily
                        that.userList.empty();
                        $.each(result.d, function () {
                            that._showSingleUser(this.UserName);
                        });

                        that.userList.accordion('destroy').accordion({
                            autoHeight: false,
                            navigation: true,
                            collapsible: true
                        });
                    }

                    // Start polling for online users
                    window.setTimeout(function () {
                        that.element.queue("onlineUsersRequest", that._getOnlineUsers());
                    }, 5000);

                    // Dequeue the next request
                    that.element.dequeue("onlineUsersRequest");
                }
            });
        },

        _showSingleUser: function (userName) {
            $("#users").append('<h3><a href="#">' + userName + '</a></h3>');
            $("#users").append('<div></div>');
        },

        _getMessages: function () {
            var that = this;

            this.options.sendAjaxRequest({
                type: "POST",
                url: that.options.getMessagesUrl,
                data: "{}",
                contentType: "application/json; charset=utf-8",
                dataType: "json",

                success: function (result) {
                    if (result.d != null) {
                        $.each(result.d, function () {
                            // Show message
                            that._showSingleMessage(this.Sender, this.Content, this.SubmitDateTime);
                            // Set latest message
                            if ((that.latestMessage.MessageID < this.MessageID) || (that.latestMessage.MessageID === null)) {
                                that.latestMessage.MessageID = this.MessageID;
                                that.latestMessage.Sender = this.Sender;
                                that.latestMessage.Content = this.Content;
                                that.latestMessage.SubmitDateTime = this.SubmitDateTime;
                            };
                        });
                    }

                    // Scroll to bottom
                    that._scrollToBottom();

                    // Start polling for new messages
                    window.setTimeout(function () { that._getNewMessages(); }, 1000);
                }
            });
        },

        _getNewMessages: function () {
            var that = this;

            this.options.sendAjaxRequest({
                type: "POST",
                url: that.options.getNewMessagesUrl,
                data: JSON.stringify({ messageId: that.latestMessage.MessageID }),
                contentType: "application/json; charset=utf-8",
                dataType: "json",
                timeout: 60000, // Set timeout 1 min for long polling
                error: function () {
                    that.element.queue("messageRequest", that._getNewMessages());
                    that.element.dequeue("messageRequest");
                },
                success: function (result) {
                    if (result.d != null) {
                        $.each(result.d, function () {
                            that._showSingleMessage(this.Sender, this.Content, this.SubmitDateTime);
                            if (that.latestMessage.MessageID < this.MessageID) {
                                that.latestMessage.MessageID = this.MessageID;
                                that.latestMessage.Sender = this.Sender;
                                that.latestMessage.Content = this.Content;
                                that.latestMessage.SubmitDateTime = this.SubmitDateTime;
                            };

                            if (!that.isMouseOver) {
                                that.unreadMessageCount += 1;
                            }

                            that._scrollDownOneMessage(25);
                        });

                        if (that.unreadMessageCount > 0) {
                            if (!that.isMouseOver) {
                                that.options.alertNewMessages(that.unreadMessageCount);
                            }
                        }
                    }

                    // Start polling for new messages
                    window.setTimeout(function () {
                        // Queue ajax request for new messages, dequeue after any previous ajax request successed.
                        that.element.queue("messageRequest", that._getNewMessages());
                    }, 1000);

                    // Dequeue the next ajax message request
                    that.element.dequeue("messageRequest");
                }
            });
        },

        _showSingleMessage: function (sender, content, datetime) {
            // Convert url string into link
            var modifiedContent = replaceURLWithHTMLLinks($('<div/>').text(content).html());
            // Convert the datetime string into user friendly format
            var unixTimeStr = datetime.substr(6, 13);
            var theTime = new Date(parseInt(unixTimeStr));
            var modifiedDatatime = theTime.getHours() + ":" + theTime.getMinutes() + ":" + theTime.getSeconds();

            this.messageList.append("<tr><td class='message-sender'>" + sender + "</td><td class='message-content'>" + modifiedContent + "</td><td class='message-datetime'>" + modifiedDatatime + "</td><tr>");
        },

        _submitMessage: function (newMessage) {
            var that = this;
            this.options.sendAjaxRequest({
                type: "POST",
                url: that.options.submitMessageUrl,
                data: JSON.stringify({ content: newMessage }),
                contentType: "application/json; charset=utf-8",
                dataType: "json",
                error: function () {
                    that._clearNewMessage();
                    that._submitMessage(newMessage);
                },
                success: function () {
                    // Clear textarea
                    that._clearNewMessage();
                }
            });
        },

        _clearNewMessage: function () {
            this.newMessage.val("");
        },

        _bindEvents: function () {
            var that = this;

            this.element.bind("mouseover." + this.name, function () {
                that.options.stopAlert();
                that.unreadMessageCount = 0;
                that.isMouseOver = true;
                that._toggleChatroom(true);
            });

            this.element.bind("mouseleave." + this.name, function () {
                that.isMouseOver = false;
                that._toggleChatroom(false);
            });

            // When user press Enter
            this.newMessage.bind("keypress", function (e) {
                var code = e.which;
                // keycode: Enter      
                if (code === 13) {

                    // Disable linebreaks
                    e.preventDefault();

                    // textarea is neither empty nor containing only white-space
                    if ($.trim(that.newMessage.val())) {
                        that._submitMessage(that.newMessage.val());
                    }
                }
            });
        },

        _toggleChatroom: function (visible) {
            if (visible === true) {
                this.element.stop();    //Stop previous animation
                this.element.animate({ "opacity": 1 });
                //                this.element.fadeIn();
            }
            else {
                this.element.stop();    //Stop previous animation
                this.element.animate({ "opacity": 0 });
                //                this.element.fadeOut();
            }
        },

        _scrollToBottom: function () {
            var that = this;
            this.messageContainer.scrollTop(that.messageContainer.prop("scrollHeight") - that.messageContainer.height());
            //this.messageContainer.animate({ scrollTop: that.messageContainer.prop("scrollHeight") - that.messageContainer.height() }, 3000);
        },

        _scrollDownOneMessage: function (height) {
            var that = this;
            this.messageContainer.scrollTop(that.messageContainer.scrollTop() + height);
            //this.messageContainer.animate({ scrollTop: that.messageContainer.attr("scrollTop") + height }, 3000);
        },

        destroy: function () {
            $.Widget.prototype.destroy.call(this);
        }

    });

} (jQuery));